using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework;
using Xyne.Utilities;
using Xyne.Events;
using Xyne.Actions;

namespace Xyne.Network
{
    public class ClientComponent : NetworkComponent
    {
        public delegate void SessionsFoundDelegate();
        private SessionsFoundDelegate sessionSearchCallback = null;
        private AvailableNetworkSessionCollection availableSessions;
        private IAsyncResult sessionSearch;
        private PacketReader reader;

        public AvailableNetworkSessionCollection AvailableSessions
        {
            get { return availableSessions; }
        }

        public ClientComponent(Game game)
            : base(game)
        {
            Wizard = GameObjects.RightWizard;
            reader = new PacketReader();
        }

        /// <summary>
        /// The bottleneck where all network-based events go (i.e. game ending)
        /// </summary>
        /// <param name="e">The event</param>
        public override void HandleEvent(Event e)
        {
            if (e is GameQuitEvent || e is StartCastingEvent || e is StopCastingEvent || e is GamePauseEvent)
            {
                DataQueue.Enqueue(e);
            }
            else if (e is GameWonEvent)
            {
                // Do nothing, clients cannot declare that they have won.
            }
            else if (e is GameLostEvent)
            {
                GameLostEvent lost = e as GameLostEvent;
                // Only send if the player lost due to puzzle board filled
                if (lost.Reason == GameLostEvent.PuzzleBoardFilled)
                {
                    DataQueue.Enqueue(e);
                }
            }
            else
            {
                // Default to local event handling
                EventManager.Instance.SendEvent(e);
            }
        }

        /// <summary>
        /// The bottleneck where all network-based actions go (i.e. game fireball)
        /// </summary>
        /// <param name="e">The action</param>
        public override void HandleAction(Action a)
        {
            DataQueue.Enqueue(a);
        }

        public void SearchForSessions(SessionsFoundDelegate callback)
        {
            sessionSearchCallback = callback;
            sessionSearch = NetworkSession.BeginFind(ConnectionType, 1, null, new AsyncCallback(SessionsFound), null);
        }

        public void SessionsFound(IAsyncResult result)
        {
            if ((sessionSearch != null) && sessionSearch.IsCompleted)
            {
                availableSessions = NetworkSession.EndFind(result);

                if (sessionSearchCallback != null)
                    sessionSearchCallback();
            }
        }

        public void JoinSession(AvailableNetworkSession session)
        {
            Session = NetworkSession.Join(session);

            if (Session.LocalGamers.Count > 0)
                Gamer = Session.LocalGamers[0];
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            if (Session != null && Gamer != null)
            {
                while (Gamer.IsDataAvailable)
                {
                    NetworkGamer sender;
                    Gamer.ReceiveData(reader, out sender);

                    if (!sender.IsLocal)
                    {
                        while (reader.PeekChar() != -1)
                        {
                            ulong type = reader.ReadUInt64();
                            Console.WriteLine(type);
                            String gamer = reader.ReadString(); // The player that this came from

                            switch ((NetworkDataType)type)
                            {
                                case NetworkDataType.GameQuit:
                                    EventManager.Instance.SendEvent(new GameQuitEvent(this, gamer));
                                    break;
                                case NetworkDataType.GamePause:
                                    GamePauseEvent pause = GamePauseEvent.Deserialize(reader);
                                    EventManager.Instance.SendEvent(pause);
                                    break;
                                case NetworkDataType.GameLost:
                                    EventManager.Instance.SendEvent(GameLostEvent.Deserialize(reader));
                                    break;
                                case NetworkDataType.GameWon:
                                    EventManager.Instance.SendEvent(GameWonEvent.Deserialize(reader));
                                    break;
                                case NetworkDataType.GameState:
                                    int numEntities = reader.ReadInt32();
                                    for (int i = 0; i < numEntities; i++)
                                    {
                                        ulong id = reader.ReadUInt64();
                                        foreach (StateEntity entity in StateEntities)
                                        {
                                            if (entity.EntityType == (EntityType)id)
                                            {
                                                entity.SetState(reader);
                                            }
                                        }
                                    }

                                    break;
                            }
                        }
                    }
                }
            }
        }
    }
}
